package jdklearning.concurrent.thread.yield;

/**
 * 在单线程场景下，这段代码执行起来是没有问题的。但是在多线程并发场景下，由不同的线程create和get东西
 * ，这段代码是有问题的。问题的原因与普通的双重检查锁定单例模式(Double Checked Locking, DCL)10类似
 * ，即SomeThing的构建与将指向构建中的SomeThing引用赋值到object变量这两者可能会发生重排序。
 * 导致get中返回一个正被构建中的不完整的SomeThing对象实例。为了解决这一问题
 * ，通常的办法是使用volatile修饰object字段。这种方法避免了重排序，保证了内存可见性
 * ，摒弃比使用同步块导致的性能损失更小。但是，假如使用场景对object的内存可见性并不敏感的话（不要求一个线程写入了object，object的新值立即对下一个读取的线程可见）
 * ，在Intel 64/IA-32环境下，有更好的解决方案。
 *
 *  根据上一章的内容，我们知道Intel 64/IA-32下写操作之间不会发生重排序，即在处理器中
 *  ，构建SomeThing对象与赋值到object这两个操作之间的顺序性是可以保证的。这样看起来
 *  ，仅仅使用volatile来避免重排序是多此一举的。但是，Java编译器却可能生成重排序后的指令。
 *  但令人高兴的是，Oracle的JDK中提供了Unsafe. putOrderedObject，Unsafe. putOrderedInt，Unsafe. putOrderedLong
 *  这三个方法，JDK会在执行这三个方法时插入StoreStore内存屏障，避免发生写操作重排序。
 *  而在Intel 64/IA-32架构下，StoreStore屏障并不需要，Java编译器会将StoreStore屏障去除。
 *  比起写入volatile变量之后执行StoreLoad屏障的巨大开销，采用这种方法除了避免重排序而带来的性能损失以外
 *  ，不会带来其它的性能开销。
 *
 *
 * @author shenenlu 2021年06月04日 上午09:50:01
 */
public class Container {
    public static class SomeThing {
        private int status;

        public SomeThing() {
            status = 1;
        }

        public int getStatus() {
            return status;
        }
    }

    private SomeThing object;

    public void create() {
        object = new SomeThing();
    }

    public SomeThing get() {
        while (object == null) {
            Thread.yield(); //不加这句话可能会在此出现无限循环
        }
        return object;
    }
}
